﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using TrainGame.Model;

namespace TrainGame.AI
{
	/// <summary>
	/// Base class for all AI goals. Defines the common interface all goals must support.
	/// </summary>
	/// <remarks>
	/// Eventually will support
	///		tile lay goals
	///		token goals
	///		train purchase goals
	///		stock round goals
	///	And we'll need a way to relate goals to each other (OR actions based on a planned SR dump, for example.)
	/// </remarks>
	abstract internal class AiGoal
	{
		/// <summary>
		/// Friendly name of this goal, for trace output
		/// </summary>
		abstract public string GetName(PublicCompany company);

		/// <summary>
		/// What is this goal's "need" value - how important is serving this goal?
		/// </summary>
		/// <param name="extraInfo">extra parameters unique to the type of goal</param>
		/// <returns>value of this goal's 'need'</returns>
		abstract public int ComputeNeedValue(params object[] extraInfo);

		/// <summary>
		/// Determine the best move for this goal.
		/// </summary>
		/// <remarks>
		/// Derived classes are expected to watch the stopwatch and abort processing if the stopwatch runs out.
		/// </remarks>
		/// <param name="watch">a running stopwatch</param>
		/// <param name="timeLimit">if timelimit exceeded, return immediately</param>
		/// <param name="extraInfo">extra parameters unique to the type of goal</param>
		/// <returns>a list of the AI's best moves (caller can choose which to apply, if more than one)</returns>
		abstract public List<AiMove> ComputeMove(Stopwatch watch, TimeSpan timeLimit, params object[] extraInfo);

		/// <summary>
		/// Look for an entry in extraInfo that is the specified type
		/// </summary>
		/// <typeparam name="T">the type to look for</typeparam>
		/// <param name="extraInfo">the parameter array passed to ComputeMove or other</param>
		/// <returns>the parameter if found, else default(T)</returns>
		static protected T GetParameter<T>(object[] extraInfo)
		{
			return GetParameter<T>(extraInfo, 1);
		}

		/// <summary>
		/// Look for an entry in extraInfo that is the specified type
		/// </summary>
		/// <typeparam name="T">the type to look for</typeparam>
		/// <param name="extraInfo">the parameter array passed to ComputeMove or other</param>
		/// <param name="instance">which instance of that type to find, 1=1st, 2=2nd, etc.</param>
		/// <returns>the parameter if found, else default(T)</returns>
		static protected T GetParameter<T>(object[] extraInfo, int instance)
		{
			foreach (object o in extraInfo)
			{
				if (o is T)
				{
					if (--instance <= 0)
						return (T)o;
				}
			}
			return default(T);
		}

		/// <summary>
		/// set and used by caller, and by sorter
		/// </summary>
		public int Need;

		internal static void SortHighestToLowest(List<AiGoal> Goals)
		{
			Goals.Sort(new AiGoalSorter(true));
		}

		public override string ToString()
		{
			return string.Format("{0} {1}", GetType(), Need);
		}
	}

	internal class AiGoalSorter : IComparer<AiGoal>
	{
		public bool HighestFirst = true;

		public AiGoalSorter(bool highestFirst)
		{
			HighestFirst = highestFirst;
		}

		#region IComparer<AiGoal> Members

		/// <summary>
		/// Compares two AiGoals based on their .Needs member.
		/// 
		/// This function respects the HighestFirst member variable.
		/// Use it to force highest-first or lowest-first sorting.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns>
		///		-1 for x-before-y
		///		+1 for y-before-x
		///		 0 for x = y
		/// </returns>
		public int Compare(AiGoal x, AiGoal y)
		{
			if (x.Need == y.Need)
				return 0;

			if (HighestFirst)
			{
				// we want the higher value
				if (x.Need > y.Need)
					return -1;	// x is higher
				return +1;		// y is higher
			}
			else
			{
				// we want the lower value
				if (x.Need < y.Need)
					return -1;	// x is lower
				return +1;		// y is lower
			}

		#endregion
		}
	}
}
